﻿using System.Text.Json;

namespace Tree;

class Program
{
    private static Config _config = GetDefaultConfig();
    static void Main(string[] args)
    {
        InitConfig();
        Console.OutputEncoding = System.Text.Encoding.UTF8;
        List<string> argList = Utils.GetArgsList(args);
        if(!GetArgs(argList, out string aimDir, out int maxLevel))
            return;
        //获取当前控制台宽度
        int maxLength = Console.WindowWidth;//减1避免全角字符显示不全
        
        int curLevel = 0;
        //WriteLine("", "📂 ", aimDir+"🛸", "|------后缀名", "|----修改时间---", "|---大小--|", maxLength, ConsoleColor.Yellow, '-');
        Utils.WriteLine("📂"+aimDir+"🛸", ConsoleColor.Yellow);
        ShowDir(aimDir, curLevel, maxLevel, maxLength, true, "");
    }
    private static void ShowDir(string curDir, int curLevel, int maxLevel, int maxLength, bool isLast, string space)
    {
        if(curLevel > 7)
        {
            string levelText = space + (isLast? "└─": "├─");  
            Utils.Write(levelText, ConsoleColor.Yellow);
            Utils.Write(" ...[层级过多]", ConsoleColor.Yellow);
            Utils.WriteLine("");
            return;
        }
        if(curLevel > 0)
        {
            PrintDir(curDir, curLevel, maxLength, isLast, space);
        }
        curLevel++; 
        if(curLevel > maxLevel && maxLevel!= -1)
            return;

        List<string> dirList = Directory.GetDirectories(curDir).ToList();
        List<string> fileList = Directory.GetFiles(curDir).ToList();
        int dirCount = dirList.Count;
        int fileCount = fileList.Count;
        if(curLevel > 1)
            space = space +(isLast? "  " : "│ ");

        for(int i = 0; i < dirCount; i++)
        {
            string dir = dirList[i];
            bool isLastDir = i == dirCount-1 && fileCount == 0;
            ShowDir(dir, curLevel, maxLevel, maxLength, isLastDir, space);
        }
        for(int i = 0; i < fileCount; i++)
        {
            string file = fileList[i];
            PrintFile(file, curLevel, maxLength, i == fileCount-1, space);
        }
    }

    private static void PrintDir(string curDir, int curLevel, int maxLength, bool isLast, string space)
    {
        DirectoryInfo directoryInfo = new DirectoryInfo(curDir);
        DateTime lastModifiedTime = directoryInfo.LastWriteTime;
        string dirName = directoryInfo.Name;
        string icon = "📁 ";        
        string levelText = space + (isLast? "└─": "├─");
        string timeStr = lastModifiedTime.ToString("yyyy/MM/dd HH:mm");
        WriteLine(levelText, icon, dirName, "", timeStr, "", maxLength, ConsoleColor.Green);
    }
    private static void PrintFile(string curFile, int curLevel, int maxLength, bool isLast, string space)
    {
        FileInfo fileInfo = new FileInfo(curFile);
        DateTime lastModifiedTime = fileInfo.LastWriteTime;
        string fileName = fileInfo.Name;
        string fileExt = fileInfo.Extension;
        string fileSize = GetFileSizeWithUnit(fileInfo.Length);
        string icon = "📄 ";
        if(_config.FileIcons.ContainsKey(fileExt))
            icon = _config.FileIcons[fileExt] + " ";
        
        string levelText = space + (isLast? "└─": "├─");
        string timeStr = lastModifiedTime.ToString("yyyy/MM/dd HH:mm");
        WriteLine(levelText, icon, fileName, fileExt, timeStr, fileSize, maxLength, ConsoleColor.Green);
    }

    private static void WriteLine(string levelText, string icon, string fileName, string fileExt, string timeStr, string sizeStr, int maxLength, ConsoleColor color, char fillChar = ' ')
    {
        int maxExtsLength = 20;
        int maxTimeLength = 16;
        int maxSizeLength = 12;        
        int maxNameLength = maxLength;
        string extsText = string.Empty;
        if(maxLength > (30 + maxExtsLength))
        {
            extsText = GetText(fileExt, maxExtsLength, fillChar, true);
            maxNameLength = maxNameLength - maxExtsLength;
        }
        string timeText = string.Empty;
        if(maxLength > (30 + maxExtsLength + maxTimeLength))
        {
            timeText = GetText(timeStr, maxTimeLength, fillChar, false);
            maxNameLength = maxNameLength - maxTimeLength;
        }
        string sizeText = string.Empty;
        if(maxLength > (30 + maxExtsLength + maxTimeLength + maxSizeLength))
        {
            sizeText = GetText(sizeStr, maxSizeLength, fillChar, false);
            maxNameLength = maxNameLength - maxSizeLength;
        }

        maxNameLength = maxNameLength - levelText.Length - 3;
        string nameText = icon + GetText(fileName, maxNameLength, fillChar, true);
        Utils.Write(levelText, ConsoleColor.Yellow);
        Utils.Write(nameText, color);
        Utils.Write(extsText, color);
        Utils.Write(timeText, ConsoleColor.DarkGray);
        Utils.Write(sizeText, ConsoleColor.DarkGray);
        Utils.WriteLine("");
    }

    private static string GetFileSizeWithUnit(long size)
    {
        string[] units = { "B", "KB", "MB", "GB", "TB" };
        int unitIndex = 0;
        while (size >= 1024 && unitIndex < units.Length - 1)
        {
            size /= 1024;
            unitIndex++;
        }
        return $"{size}{units[unitIndex]}"; 
    }
    // 按固定长度截取文本
    static string GetText(string text, int maxLength, char fillChar = '*', bool isLeft = true)
    {
        string result = "";
        string head = "";
        string tail = "";
        int headLength = 0;
        int actualLength = 0;
        for(int i = 0; i < maxLength; i++)
        {
            char c = fillChar;
            int charWidth = 1;
            if(i < text.Length)
            {
                c = text[i];                
                if(char.GetUnicodeCategory(c) == System.Globalization.UnicodeCategory.OtherLetter || (c >= 0xFF01 && c <= 0xFF5E)) 
                    charWidth = 2;
                actualLength += charWidth;
                if(actualLength < maxLength-3)
                {
                    head += c;
                    headLength += charWidth;
                }
                else if(actualLength <= maxLength)
                {
                    tail += c;
                }
            } 
        }
        int padding = maxLength - actualLength;
        if(padding >= 0)
        {
            string fillStr = string.Join("", Enumerable.Repeat(fillChar, padding));
            if(isLeft)
                result = head + tail + fillStr;
            else
                result = fillStr + head + tail;
        }
        else
        {
            result = head + string.Join("", Enumerable.Repeat('.', maxLength - headLength));
        }
        return result;
    }


    private static void InitConfig()
    {
        try
        {
            Config? config = null;
            string appPath = AppDomain.CurrentDomain.BaseDirectory;
            string configPath = Path.Join(appPath, "config.json");
            if(File.Exists(configPath))
            {
                string jsonStr = File.ReadAllText(configPath);
                config = JsonSerializer.Deserialize<Config>(jsonStr); 
            }
            else
            {
                string jsonStr = JsonSerializer.Serialize(_config);
                File.WriteAllText(configPath, jsonStr); 
            }

            if(config != null)
            {
                _config.DefaultLevel = config.DefaultLevel;
                foreach(var item in config.FileIcons)
                {
                    _config.FileIcons[item.Key] = item.Value;
                }
            }
        }
        catch(Exception ex)
        {
            Utils.WriteLine(ex.ToString(), ConsoleColor.Red);
        }
        
    }
    private static Config GetDefaultConfig()
    {
        Config config = new Config();
        config.DefaultLevel = -1;
        config.FileIcons = new Dictionary<string, string>();
        config.FileIcons.Add(".txt", "🗒️");
        config.FileIcons.Add(".md", "📔");
        config.FileIcons.Add(".json", "📋");
        config.FileIcons.Add(".jpg", "🖼️");
        config.FileIcons.Add(".png", "🖼️");
        config.FileIcons.Add(".bmp", "🖼️");
        config.FileIcons.Add(".mp3", "🎶");
        config.FileIcons.Add(".mp4", "🎞️");
        config.FileIcons.Add(".mkv", "🎞️");
        config.FileIcons.Add(".avi", "🎞️");
        return config;
    }
    private static bool GetArgs(List<string> args, out string aimDir,out int level)
    {
        aimDir = Directory.GetCurrentDirectory();
        level = _config.DefaultLevel;
        if(args.Count > 0)
        {
            var levelTag = args.Find(x => x.ToLower()=="-l");
            if(levelTag != null && args.IndexOf(levelTag) + 1 < args.Count)
            {
                var levelStr = args[args.IndexOf(levelTag) + 1];
                if(int.TryParse(levelStr, out int levelValue))
                {
                    level = levelValue; 
                    args.Remove(levelTag);
                    args.Remove(levelStr);
                } 
                else
                {
                    Utils.WriteLine("-l 参数错误", ConsoleColor.Red);
                    return false;
                }
            }
            if(args.Count > 0)
            {
                string dirStr = args[0];
                if(Directory.Exists(dirStr))
                {
                    aimDir = dirStr;
                }
                else
                {
                    Utils.WriteLine($"路径 [{dirStr}] 不存在", ConsoleColor.Red);
                    return false;
                }
            }
                
        }
        return true;
    }
}

public class Config
{
    public int DefaultLevel { get; set; }
    public Dictionary<string, string> FileIcons { get; set; }
}

public static class Utils
{
    public static List<string> GetArgsList(string[] args)
    {
        List<string> argsList = new List<string>();
        for (int i = 0; i < args.Length; i++)
        {
            argsList.Add(args[i]);
        }
        return argsList;
    }

    public static void WriteLine(string text, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
    {
        if (backColor.HasValue)
            Console.BackgroundColor = backColor.Value;
        if (foreColor.HasValue)
            Console.ForegroundColor = foreColor.Value;
        Console.WriteLine(text);
        Console.ResetColor();
    }

    public static void Write(string text, ConsoleColor? foreColor = null, ConsoleColor? backColor = null)
    {
        if (backColor.HasValue)
            Console.BackgroundColor = backColor.Value;
        if (foreColor.HasValue)
            Console.ForegroundColor = foreColor.Value;
        Console.Write(text);
        Console.ResetColor();
    }
}